#include<iostream>
#include <arpa/inet.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<string>
#include<cstring>
#include <sys/socket.h>
using namespace std;

enum ExitCode
{
    USEERR = 1,
    SOCKERR
};

enum class Status
{
     NEW, //刚开始
     CONNECTIING,
     CONNECTED,
     DISCONNECTED,//链接失联
     CLOSED //链接关闭 
};

const static int defaultsockfd = -1;
const static int defaultinterval = 1;
const static int defaultmaxretries = 5;


class ClientConnection
{
public:
    ClientConnection(uint16_t port,const string& ip)
    :_sockfd(defaultsockfd),
    _ip(ip),
    _port(port),
    _status(Status::NEW),
    _retry_interval(defaultinterval),
    _max_retries(defaultmaxretries)
    {}

    ~ClientConnection(){}

    void Connect()
    {
        //1.创建套接字
        _sockfd = ::socket(AF_INET,SOCK_STREAM,0);
        if(_sockfd < 0)
        {
            cerr << "create socked failed" << endl;
            exit(ExitCode::SOCKERR);
        }
        //2.建立连接
         //2.1 填充字段
        struct sockaddr_in server;
        memset(&server,0,sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_port); //主机转网络序列
        inet_pton(AF_INET,_ip.c_str(),&server.sin_addr);
        //p:process n:net 先字符串转点分十进制,再转网络序列
         //2.2 建立连接
        int n = ::connect(_sockfd,(struct sockaddr*)&server,sizeof(server));
        if(n < 0)
        {
            //1.关闭fd
            DisConnect();
            //2.更新状态:进行重连
            _status = Status::DISCONNECTED;  
            //3.返回
            return;
        }
        //连接成功
        _status = Status::CONNECTED;
    }

    void DisConnect()
    {
         if(_sockfd > defaultsockfd)
         {
             //1.关闭文件描述符
             ::close(_sockfd);
             //2.更新状态
             _status = Status::CLOSED;
             //3.重新设置sockfd为默认
             _sockfd = -1;
         }
    }

    void ReConnect()
    {
        int count = 0;
        while(count < _max_retries)
        {
            //每次连接
            Connect();
            //根据状态进行判断每次的重连结果
            if(_status == Status::CONNECTED) //重连成功
                return;
            count++;
            cout << "重连次数：" << count << " 最大上限为：" << _max_retries << endl;
            sleep(_retry_interval); //重连间隔
        } 
        //到这里表示重连失败:更新状态关闭连接
        cout << "重连失败..." << endl;
        _status = Status::CLOSED;
    }

    void Process()//通信
    {
        while(true)
        {
            string inbuffer; //要发的消息
            getline(cin, inbuffer);
            if(inbuffer.empty()) continue;
            ssize_t n = ::send(_sockfd,inbuffer.c_str(),inbuffer.size(),0);
            if(n > 0)
            {
               //发送成功就可以读取
               char buffer[1024];
               ssize_t m = ::recv(_sockfd,buffer,sizeof(buffer)-1,0);
               if(m > 0)
               {
                  buffer[m] = 0;
                  cout << "server# " << buffer;
               }
               else  //掉线/关闭/读取出错
               {
                  _status = Status::DISCONNECTED; //认为链接断开进行重连
                  break;
               }
            }
            else  //发送失败:关闭链接
            {
                 cerr << "send error!" << endl;
                 _status = Status::CLOSED;
                 break;
            }
        }
    }

    Status GetStatus() ///方便TcpClinet获取状态
    {
        return _status;
    }

private:
    int _sockfd;
    string _ip;
    uint16_t _port;
    Status _status;
    int _retry_interval;//重连时间间隔
    int _max_retries;//最多的重连次数
};


class TcpClient
{

public:
    TcpClient(uint16_t port,const string& ip)
    :_conn(port,ip)
    {}

    ~TcpClient(){}

    void Excute()
    {
        while(true)
        {
            switch(_conn.GetStatus())
            {
             case Status::NEW: //刚开始进行连接
                   cout << "开始进行连接..." << endl;
                   _conn.Connect();
                   break;
             case Status::CONNECTED: //连接成功就执行任务
                   cout << "连接成功,开始执行任务..." << endl; 
                   _conn.Process();
                   break;
             case Status::DISCONNECTED:
                   cout << "掉线开始进行重连..." << endl;
                   _conn.ReConnect();
                   break;
             case Status::CLOSED:
                   cout << "开始关闭链接.." << endl;
                   _conn.DisConnect();
                   return; //注意这里是return 直接返回
             default:
                   break;           
            }
        }
    }

private:
   ClientConnection _conn;//表示链接

};

void Usage(string str)
{
     cout << "Usage: " << str << " serverip serverport" << endl;
}


int main(int argc,char* argv[])
{
    if(argc != 3)
    {
         Usage(argv[0]);
         exit(ExitCode::USEERR);
    }
    string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);  
    TcpClient client(serverport,serverip);
    client.Excute();
    return 0;
}